package impl

import (
	"errors"
	"github.com/gorilla/websocket"
	"log"
	"sync"
)

type Connection struct {
	wsConn  *websocket.Conn
	inChan  chan []byte
	outChan chan []byte

	mutex     sync.Mutex
	isClosed  bool
	closeChan chan []byte
}

func InitConnection(wsConn *websocket.Conn) (conn *Connection, err error) {
	conn = &Connection{
		wsConn:    wsConn,
		inChan:    make(chan []byte, 10),
		outChan:   make(chan []byte, 10),
		closeChan: make(chan []byte, 1),
	}
	//启动读协成
	go conn.readloop()
	go conn.writeLoop()
	return conn, nil
}


func (conn *Connection) ReadMessage() (data []byte, err error) {
	select {
		case data = <-conn.inChan:
			return data, nil
		case <-conn.closeChan:
			return nil, errors.New("websocket closed")
	}
}

func (conn *Connection) WriteMessage(data []byte) (err error) {
	select {
		case conn.outChan <- data:
		case <-conn.closeChan:
			return errors.New("websocket closed")
	}
	return nil
}

func (conn *Connection) Close() {
	log.Println("close")
	//线程安全
	conn.wsConn.Close()

	conn.mutex.Lock()
	defer conn.mutex.Unlock()
	if conn.isClosed != true {
		close(conn.closeChan)
		conn.isClosed = true
	}

}

//读取数据，写入inchan
func (conn *Connection) readloop() {
	var (
		data []byte
		err  error
	)
	for {
		if _, data, err = conn.wsConn.ReadMessage(); err != nil {
			log.Println("read error!")
			if err != nil {
				conn.Close()
				break
			}
		}
		log.Println("read:" + string(data))
		select {
		case conn.inChan <- data:
		case <-conn.closeChan:
			//closeChan关闭的时候,会进入
			conn.Close()
			break
		}
	}
	//ERR:
	//conn.Close()
}

//从outchan读数据，发送出去

func (conn *Connection) writeLoop() {
	var (
		data  []byte
		err   error
		right bool
	)
	log.Println("write up!")
	for {
		select {
		case data, right = <-conn.outChan:
			if right != true {
				log.Println("write error!")
				panic(right)
			}
		case <-conn.closeChan:
			defer conn.Close()
			break;

		}
		log.Println("write:" + string(data))
		if err = conn.wsConn.WriteMessage(websocket.TextMessage, data); err != nil {
			if err != nil {
				conn.Close()
				break
				//goto ERR
			}
		}
	}
	//ERR:
	//conn.Close()
}
